/* 
 * File:   RenderingSystem.cpp
 * Author: aljosa
 * 
 * Created on April 28, 2010, 11:19 PM
 */

#include "RenderingSystem.h"

#include "messages.h"

namespace xEngine
{
    namespace Renderer
    {
        RenderingSystem *RenderingSystem::instance = NULL;
        // 
        // TODO: MOVE TO BASIC_GEOMETRY CLASS
        //
              		  float box[] = {
				// FRONT
				-0.5f, -0.5f,  0.5f,
				 0.5f, -0.5f,  0.5f,
				-0.5f,  0.5f,  0.5f,
				 0.5f,  0.5f,  0.5f,

				// BACK
				-0.5f, -0.5f, -0.5f,
				-0.5f,  0.5f, -0.5f,
				 0.5f, -0.5f, -0.5f,
				 0.5f,  0.5f, -0.5f,

				// LEFT
				-0.5f, -0.5f,  0.5f,
				-0.5f,  0.5f,  0.5f,
				-0.5f, -0.5f, -0.5f,
				-0.5f,  0.5f, -0.5f,

				// RIGHT
				 0.5f, -0.5f, -0.5f,
				 0.5f,  0.5f, -0.5f,
				 0.5f, -0.5f,  0.5f,
				 0.5f,  0.5f,  0.5f,

				// TOP
				-0.5f,  0.5f,  0.5f,
				 0.5f,  0.5f,  0.5f,
				 -0.5f,  0.5f, -0.5f,
				 0.5f,  0.5f, -0.5f,

				// BOTTOM
				-0.5f, -0.5f,  0.5f,
				-0.5f, -0.5f, -0.5f,
				 0.5f, -0.5f,  0.5f,
				 0.5f, -0.5f, -0.5f,
			};

			float texCoords[] = {
				// FRONT
				 0.0f, 0.0f,
				 1.0f, 0.0f,
				 0.0f, 1.0f,
				 1.0f, 1.0f,

				// BACK
				 1.0f, 0.0f,
				 1.0f, 1.0f,
				 0.0f, 0.0f,
				 0.0f, 1.0f,

				// LEFT
				 1.0f, 0.0f,
				 1.0f, 1.0f,
				 0.0f, 0.0f,
				 0.0f, 1.0f,

				// RIGHT
				 1.0f, 0.0f,
				 1.0f, 1.0f,
				 0.0f, 0.0f,
				 0.0f, 1.0f,

				// TOP
				 0.0f, 0.0f,
				 1.0f, 0.0f,
				 0.0f, 1.0f,
				 1.0f, 1.0f,

				// BOTTOM
				 1.0f, 0.0f,
				 1.0f, 1.0f,
				 0.0f, 0.0f,
				 0.0f, 1.0f
                        };


        RenderingSystem::RenderingSystem() {
            // TODO
        }


        RenderingSystem::~RenderingSystem() {
            // TODO
        }

       /* static RenderingSystem::RenderingSystem& get() {
            static RenderingSystem sSingleton; // Will be constructed only on the first call
            return sSingleton;
        }*/

        RenderingSystem *RenderingSystem::get()
        {
            //static RenderingSystem instance; // only instantiated when getInstance() is called
           // return instance;
            if (instance == NULL) // Is this the first call?
            {
                instance = new RenderingSystem;
            }

            return instance;
        }


        bool RenderingSystem::init() {

            // Basic OpenGL initialization
            glClearColor(0.9f, 0.9f, 0.9f, 0.9f); // Black Background
            glShadeModel(GL_SMOOTH);  // Enables Smooth Shading
            glEnable(GL_DEPTH_TEST);  // Enables Depth Testing
            glDepthFunc(GL_LEQUAL);   // The Type Of Depth Test To Do
          //  glFrontFace(GL_CCW);   // counterclockwise polygons are out
            glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);

            // Vertex arrays are nice, enable them
            glEnableClientState(GL_VERTEX_ARRAY);
            glEnableClientState(GL_TEXTURE_COORD_ARRAY);

            // Dump sucess into console
            Messages::initMessage("RenderingSystem", true);

            return true;
        }

        void RenderingSystem::shutDown() {
            // Clean the mess
            
            // Handle class instance
            if (instance != NULL)
            {
                delete instance;
                instance = NULL;
            }

            // Inform the system
            Messages::initMessage("RenderingSystem shutdown", true);
        }

        // =================================================
        // Rendering system interface implementation
        // =================================================



         /********************************************
          * drawSkyCube()
          * Draws simple cube
          ********************************************/
          void RenderingSystem::drawSkyCube(float x, float y, float z, float size, int textures[6])
          {

              glColor4f(1.0, 1.0, 1.0, 1.0); // color of cube
            // glBindTexture(GL_TEXTURE_2D, texID); // texture
              glDisable(GL_LIGHTING);

            // glEnable(GL_CULL_FACE);
           //  glCullFace(GL_BACK);
        //      glDisable(GL_TEXTURE_2D); // REMOVE

              glVertexPointer(3, GL_FLOAT, 0, box);
              glTexCoordPointer(2, GL_FLOAT, 0, texCoords);
                  glPushMatrix();

                    glTranslatef(x, y, z);
                    glScalef(size, size, size);

			// FRONT AND BACK
                        glBindTexture(GL_TEXTURE_2D, textures[0]);
			glNormal3f(0.0f, 0.0f, 1.0f);
			glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

                      /*  glBindTexture(GL_TEXTURE_2D, textures[1]);
			glNormal3f(0.0f, 0.0f, -1.0f);
			glDrawArrays(GL_TRIANGLE_STRIP, 4, 4);*/

			// LEFT AND RIGHT
                   /*     glBindTexture(GL_TEXTURE_2D, textures[2]);
                        glNormal3f(-1.0f, 0.0f, 0.0f);
			glDrawArrays(GL_TRIANGLE_STRIP, 8, 4);

                        glBindTexture(GL_TEXTURE_2D, textures[3]);
			glNormal3f(1.0f, 0.0f, 0.0f);
			glDrawArrays(GL_TRIANGLE_STRIP, 12, 4);

			// TOP AND BOTTOM
                        glBindTexture(GL_TEXTURE_2D, textures[4]);
			glNormal3f(0.0f, 1.0f, 0.0f);
			glDrawArrays(GL_TRIANGLE_STRIP, 16, 4);

                        glBindTexture(GL_TEXTURE_2D, textures[5]);
			glNormal3f(0.0f, -1.0f, 0.0f);
                        glDrawArrays(GL_TRIANGLE_STRIP, 20, 4);*/

                  glPopMatrix();

                 // glDisable(GL_CULL_FACE);
                  glEnable(GL_LIGHTING);
         }

        void RenderingSystem::drawDot(const Vector3 &p)
        {
            glPushMatrix();
                glBegin(GL_POINT);
                    glVertex3f(p.x, p.y, p.z);
                glEnd();
            glPopMatrix();
        }

        void RenderingSystem::drawCube(const Vector3 &p)
        {
            glVertexPointer(3, GL_FLOAT, 0, box);
        //    glTexCoordPointer(2, GL_FLOAT, 0, texCoords);
            glPushMatrix();
                glTranslatef(p.x, p.y, p.z);
                    // FRONT AND BACK
                    glNormal3f(0.0f, 0.0f, 1.0f);
                    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
                    glNormal3f(0.0f, 0.0f, -1.0f);
                    glDrawArrays(GL_TRIANGLE_STRIP, 4, 4);

                    // LEFT AND RIGHT
                    glNormal3f(-1.0f, 0.0f, 0.0f);
                    glDrawArrays(GL_TRIANGLE_STRIP, 8, 4);
                    glNormal3f(1.0f, 0.0f, 0.0f);
                    glDrawArrays(GL_TRIANGLE_STRIP, 12, 4);

                    // TOP AND BOTTOM
                    glNormal3f(0.0f, 1.0f, 0.0f);
                    glDrawArrays(GL_TRIANGLE_STRIP, 16, 4);
                    glNormal3f(0.0f, -1.0f, 0.0f);
                    glDrawArrays(GL_TRIANGLE_STRIP, 20, 4);
            glPopMatrix();
        }


        void RenderingSystem::moveTo(Vector3 p)
        {
            this->currentPos = p;
        }

        void RenderingSystem::lineTo(Vector3 p)
        {
            glPushMatrix();
                glBegin(GL_LINE);
                    glVertex3f(this->currentPos.x, this->currentPos.y, this->currentPos.z);
                    glVertex3f(p.x, p.y, p.z);
                glEnd();
            glPopMatrix();
        }

        void RenderingSystem::drawCircle(const Vector3 &p, double r, unsigned tris)
        {
            glPushMatrix();
                glTranslatef(p.x, p.y, p.z);
                unsigned it_val = 360/tris;
                //  glBegin(GL_LINE_LOOP);
                glBegin(GL_TRIANGLE_FAN);
                for (int i=0; i < 360; i+=it_val) //0.017453278 9
                {
                   double degInRad = i*DEG2RAD;
                   glVertex3f(cos(degInRad)*r,sin(degInRad)*r, 0.0);
                }
                glEnd();
           glPopMatrix();
        }

        //
        // Terrain drawing
        //
        void RenderingSystem::drawTerrain(const Vector3 &p, const Terrain &terrain) {
            // Basic, TODO: improve (vertex arrays, ...)

            glPushMatrix();
            glTranslatef(p.x, p.y, p.z);

            glColor3f(0.0f, 0.0f, 0.0f);
            // Loop over all vertices
            for ( int z=0; z<MAP_Z-1; z++ ) {
                glBegin(GL_TRIANGLE_STRIP);
                 for ( int x=0; x<MAP_X-1; x++ ) {

                     // Vertex 0
                     glColor3f(terrain.vertices[x][z][1]/255.0f, terrain.vertices[x][z][1]/255.0f, terrain.vertices[x][z][1]/255.0f);
                   //  glColor3f(1.0f, 0.0f, 0.0f);
                     glVertex3f(terrain.vertices[x][z][0], terrain.vertices[x][z][1], terrain.vertices[x][z][2]);

                     // Vertex 1
                     glColor3f(terrain.vertices[x+1][z][1]/255.0f, terrain.vertices[x+1][z][1]/255.0f, terrain.vertices[x+1][z][1]/255.0f);
                   //  glColor3f(0.0f, 1.0f, 0.0f);
                     glVertex3f(terrain.vertices[x+1][z][0], terrain.vertices[x+1][z][1], terrain.vertices[x+1][z][2]);

                                          // Vertex 3



                     
                     // Vertex 2
                     glColor3f(terrain.vertices[x][z+1][1]/255.0f, terrain.vertices[x][z+1][1]/255.0f, terrain.vertices[x][z+1][1]/255.0f);
                    // glColor3f(0.0f, 0.0f, 1.0f);
                     glVertex3f(terrain.vertices[x][z+1][0], terrain.vertices[x][z+1][1], terrain.vertices[x][z+1][2]);

                     glColor3f(terrain.vertices[x+1][z+1][1]/255.0f, terrain.vertices[x+1][z+1][1]/255.0f, terrain.vertices[x+1][z+1][1]/255.0f);
                     glVertex3f(terrain.vertices[x+1][z+1][0], terrain.vertices[x+1][z+1][1], terrain.vertices[x+1][z+1][2]);





                 }
                glEnd();
            }

            glPopMatrix();
        }



    }
}

